

#include <sudoku.h>
#include <matrix.h>

#include <stdio.h>
#include <string.h>

void _sdk_print_possibilities(sdk_square *sqr);

sudoku *sdk_load_sudoku(char *sdk_path){

    vm_matrix *tmp = vm_read_matrix(sdk_path);
  if (tmp == NULL){
#ifdef _DEBUG
    fprintf(stderr, "[sdk_load_sudoku] Could not load sudoku: %s\n", sdk_path);
#endif
    return NULL;
  }

  if (tmp->rows != SDK_WIDTH || tmp->columns != SDK_WIDTH){
#ifdef _DEBUG
    fprintf(stderr, "[sdk_load_sudoku] Invalid matrix size: [%d:%d]\n", 
	    tmp->rows, tmp->columns);
#endif
    return NULL;
  }
#ifdef _DEBUG
  fprintf(stderr, "[sdk_load_sudoku] Read matrix: good\n");
  fprintf(stderr, "[sdk_load_sudoku] Size: [%d:%d]\n", 
	  tmp->rows, tmp->columns);
#endif

  sudoku *sdk = SDK_ALLOC_SUDOKU();
  memset(sdk, 0, sizeof(sudoku));

#ifdef _DEBUG
  fprintf(stderr, "[sdk_load_sudoku] Copying mtx values\n");
#endif

  int i, j;
  for ( i = 0; i < SDK_WIDTH; i++){
    for ( j = 0; j < SDK_WIDTH; j++){
      SDK_SET_SQUARE(sdk, i, j, tmp->data[i][j]);
      SDK_GET_SQUARE(sdk, i, j).row = i;
      SDK_GET_SQUARE(sdk, i, j).col = j;
    }
  }

  vm_free_matrix(tmp);

  return sdk;

}

sudoku *sdk_copy_sudoku(sudoku *sdk){

  sudoku *new_sdk = SDK_ALLOC_SUDOKU();
  memcpy(new_sdk, sdk, sizeof(sudoku));

  int i = 0, j;
  for ( i = 0; i < SDK_WIDTH; i++){
    for ( j = 0; j < SDK_WIDTH; j++){
      SDK_SET_SQUARE(new_sdk, i, j, SDK_GET_SQUARE_VAL(sdk, i, j));
      if (SDK_GET_SQUARE(sdk, i, j).pos != NULL){
	sdk_llist_t *new_list = SDK_ALLOC_LLIST();
	memcpy(new_list, SDK_GET_SQUARE(sdk, i, j).pos, sizeof(sdk_llist_t));
	SDK_GET_SQUARE(new_sdk, i, j).pos = new_list;
      }
    }
  }

  return new_sdk;

}

void sdk_flesh_sudoku(sudoku *sdk){

  sdk_generate_lists(sdk);
  while (sdk_set_square_vals(sdk)){
    sdk_generate_lists(sdk);
  }

}

void _sdk_generate_pos_list(sudoku *sdk, int r, int c){

  if (sdk->sdk_data[r][c].val != 0){
    if (sdk->sdk_data[r][c].pos == NULL){

      sdk->sdk_data[r][c].pos = SDK_ALLOC_LLIST();
      memset(sdk->sdk_data[r][c].pos, 0, sizeof(sdk_llist_t));

      sdk_llist_add(sdk->sdk_data[r][c].pos, sdk->sdk_data[r][c].val);
      if (!sdk_llist_contains(sdk->sdk_data[r][c].pos, sdk->sdk_data[r][c].val))
	printf("ERROR: BUG: [%d:%d]\n", r, c);
    }
    return;
  }

  if (!sdk->sdk_data[r][c].pos)
    sdk->sdk_data[r][c].pos = sdk_llist_full_list();

  //printf("Generating pos list: [%d:%d]\n", r, c);

  /* Look for numbers in the row, column and submatrix to remove. */
  
  // Column first
  int i = 0;
  for ( ; i < SDK_WIDTH; i++){
    if (sdk->sdk_data[i][c].val)
      sdk_llist_remove(sdk->sdk_data[r][c].pos, sdk->sdk_data[i][c].val);
  }
  
  // Row next
  int j = 0;
  for ( ; j < SDK_WIDTH; j++){
    if (sdk->sdk_data[r][j].val)
      sdk_llist_remove(sdk->sdk_data[r][c].pos, sdk->sdk_data[r][j].val);
  }

  // Now the complicated one.
  int r_offset = ((int) r/SDK_SIZE) * SDK_SIZE;
  int c_offset = ((int) c/SDK_SIZE) * SDK_SIZE;

  i = 0;
  for ( ; i < SDK_SIZE; i++){
    j = 0;
    for ( ; j < SDK_SIZE; j++){
      if (sdk->sdk_data[r_offset + i][c_offset + j].val)
	sdk_llist_remove(sdk->sdk_data[r][c].pos, 
			 sdk->sdk_data[r_offset + i][c_offset + j].val);
    }
  }

}

void sdk_generate_lists(sudoku *sdk){

  int i = 0;
  int j;
  for ( ; i < SDK_WIDTH; i++){
    j = 0;
    for ( ; j < SDK_WIDTH; j++){
      _sdk_generate_pos_list(sdk, i, j);
    }
  }

}

sdk_square *_sdk_find_branch_square(sudoku *sdk){

  int s = 2;
  for ( ; s < SDK_WIDTH; s++){
    // Traverse the sudoku looking for a square with i or less possibilities
    int i, j;
    for ( i = 0; i < SDK_WIDTH; i++){
      for ( j = 0; j < SDK_WIDTH; j++){
	if (SDK_GET_SQUARE(sdk, i, j).val != 0) continue;
        int len = sdk_llist_size(SDK_GET_SQUARE(sdk, i, j).pos);
        if (len == 1) continue;
        if (len <= s){
          return &SDK_GET_SQUARE(sdk, i, j);
        }
      }
    }
  }

  return NULL; // If we dont find one (probably means the sudoku is solved).

}

void sdk_msg(char *place, char *msg){

  printf("[%s] %s\n", place, msg);

}

int sdk_set_square_vals(sudoku *sdk){

  int i = 0;
  int j;
  int counter = 0;
  for ( ; i < SDK_WIDTH; i++){
    j = 0;
    for ( ; j < SDK_WIDTH; j++){
      if (SDK_GET_SQUARE(sdk, i, j).pos->len == 1){
	if (SDK_GET_SQUARE(sdk, i, j).val == 0) counter++;
	SDK_SET_SQUARE(sdk, i, j, 
		       sdk_llist_lowest_num(SDK_GET_SQUARE(sdk, i, j).pos));
      }
    }
  }

  return counter;

}

void _sdk_print_sudoku_horiz_div(){

  int i = 0;

  printf("++");

  for ( ; i < SDK_SIZE; i++){
    int j = 0;
    printf("====");
    for ( ; j < SDK_SIZE-1; j++){
      printf("+====");
    }
    printf("++");
  }
  printf("\n");

}

void sdk_print_sudoku(sudoku *sdk){

  if (sdk == NULL){
    fprintf(stderr, "Sudoku is NULL\n");
    return;
  }

  int i, j, k, l;
  
  _sdk_print_sudoku_horiz_div();  

  for (i = 0; i < SDK_SIZE; i++){ // Blocks

    for (j = 0; j < SDK_SIZE; j++){ // Lines in the blocks
      printf("][ ");

      for (k = 0 ; k < SDK_SIZE; k++){ // Elements in the lines
	printf("%-2d ", SDK_GET_SQUARE_VAL(sdk, 
					 (i*SDK_SIZE)+j, (k*SDK_SIZE)));
	for (l = 1; l < SDK_SIZE; l++){
	  printf("| %-2d ", SDK_GET_SQUARE_VAL(sdk, 
					   (i*SDK_SIZE)+j, (k*SDK_SIZE)+l));
	}
	printf("][ ");	
      }
      printf("\n");

    }
  
  _sdk_print_sudoku_horiz_div();    

  }

}

void _sdk_print_possibilities(sdk_square *sqr){

  printf("[%d:%d]: (%d){ ", sqr->row, sqr->col, sqr->pos->len);

  int i = 1;
  for ( ; i < SDK_WIDTH; i++){
    if ( sdk_llist_contains(sqr->pos, i))
      printf("%d ", i);
  }

  if ( sdk_llist_contains(sqr->pos, SDK_WIDTH))
    printf("%d }\n", SDK_WIDTH);
  else 
    printf("}\n");

}

void _sdk_print_sudoku_pos_lists(sudoku *sdk){

  int i = 0;
  int j;
  for ( ; i < SDK_WIDTH; i++){
    j = 0;
    for ( ; j < SDK_WIDTH; j++){
      if (sdk->sdk_data[i][j].val == 0){
	_sdk_print_possibilities(&sdk->sdk_data[i][j]);
      }
    }
  }

}

void _sdk_print_sudoku_all_pos_lists(sudoku *sdk){

  int i = 0;
  int j;
  for ( ; i < SDK_WIDTH; i++){
    j = 0;
    for ( ; j < SDK_WIDTH; j++){
      _sdk_print_possibilities(&sdk->sdk_data[i][j]);
    }
  }

}

void sdk_print_sudoku_square(sdk_square *sqr){
  
  if (sqr == NULL){
    printf("sdk_square: (NULL)\n");
    return;
  }

  printf("sdk_square: [%d:%d] = %d | List: ", sqr->row, sqr->col, sqr->val);
  sdk_llist_print_lite(sqr->pos);
  printf("\n");

}
